from flask import current_app, jsonify, session
from flask import request, abort, make_response
from info import redis_store, constants, db
from info.utils.captcha.captcha import captcha
from info.utils.response_code import RET
from . import passport_blu
import re
from info.lib.yuntongxun.sms import CCP
from info.models import User
import random
import json
from datetime import datetime


@passport_blu.route('/login_out', methods=["POST"])
def login_out():
    """退出登录接口"""
    # 清楚session保存的用户信息
    session.pop("user_id")
    session.pop("mobile")
    session.pop("nick_name")
    # 清楚管理员数据
    session.pop("is_admin", None)

    return jsonify(errno=RET.OK, errmsg="退出登录成功")


@passport_blu.route('/login', methods=["POST"])
def login():
    """登入接口"""
    """
    1.获取参数
        1.1 手机号码 密码(未加密)
    2.效验参数
        2.1 非空判断
        2.2 手机号码格式
    3.逻辑处理
        3.1 根据手机号码查询用户对象
        3.2 对比用户填写的密码和用户对象中的密码是否一致
        3.3 一致：记录最后一次登录时间 使用session记录用户登陆信息
    4.返回值处理
    """
    # 1.1 手机号码 密码(未加密)
    params_dict = request.json
    mobile = params_dict.get("mobile")
    password = params_dict.get("password")

    # 2.1 非空判断
    if not all([mobile, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不足")

    # 2.2 手机号码格式
    if not re.match('^1[356789][0-9]{9}$', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机格式错误")

    # 3.1 根据手机号码查询用户对象
    try:
        user = User.query.filter_by(mobile=mobile).first()
    except Exception as error:
        current_app.logger.error(error)
        return jsonify(errno=RET.DBERR, errmsg="查询用户对象异常")
    if not user:
        return jsonify(errno=RET.NODATA, errmsg="用户不存在")

    # 3.2 对比用户填写的密码和用户对象中的密码是否一致
    # 会自动跟上次存储的加密密码进行对比
    if not user.check_passowrd(password):
        return jsonify(errno=RET.DATAERR, errmsg="密码填写错误")

    # 3.3 一致：记录最后一次登录时间 使用session记录用户登陆信息
    session["user_id"] = user.id
    session["mobile"] = user.mobile
    session["nick_name"] = user.nick_name
    # 修改最后一次登录的时间
    user.last_login = datetime.now()

    # 当修改了模型身上的属性的时候 不需要add只需要commit
    try:
        db.session.commit()
    except Exception as error:
        current_app.logger.error(error)
        db.session.rollback()
        return jsonify(errno=RET.DATAERR, errmsg="用户对象保存到数据库异常")

    # 4.返回值处理
    return jsonify(errno=RET.OK, errmsg="登入成功")


@passport_blu.route('/register', methods=["POST"])
def register():
    """用户注册接口"""
    """
    1.获取参数
        1.1 手机号码 用户填写的短信验证码 密码(未加密)
    2.效验参数
        2.1 非空判断
        2.2 手机号码格式判断
    3.逻辑处理
        3.1 根据手机号码拼接SMS_16620853069这个key去redis获取真实的短信验证码
        3.2 对比用户添加的短信验证码和真实的短信验证码对比
        3.3 一致：根据User模型创建用户对象,保存到数据库
        3.4 一般：用户注册成功 第一次都会给用户自动登入, 使用session存储用户信息
    4.返回值处理
    """
    # 1.1 手机号码 用户填写的短信验证码 密码(未加密)
    params_dict = request.json
    mobile = params_dict.get("mobile")
    smscode = params_dict.get("smscode")
    password = params_dict.get("password")

    # 2.1 非空判断
    if not all([mobile, smscode, password]):
        return jsonify(errno=RET.PARAMERR, errmsg="参数不足")

    # 2.2 手机号码格式判断
    if not re.match('^1[356789][0-9]{9}$', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机格式错误")

    # 3.1 根据手机号码拼接SMS_16620853069这个key去redis获取真实的短信验证码
    try:
        real_sms_code = redis_store.get("SMS_%s" %mobile)

        # 不想多次效验同一个短信验证码,当取出真实值的时候把他从数据库删除
        if real_sms_code:
            redis_store.delete("SMS_%s" %mobile)
    except Exception as error:
        current_app.logger.error(error)
        return jsonify(errno=RET.DBERR, errmsg="获取短信验证码数据库异常")
    if not real_sms_code:
        # 表示数据库的短信验证码过期了
        return jsonify(errno=RET.NODATA, errmsg="短信验证码过期")

    # 3.2 对比用户添加的短信验证码和真实的短信验证码对比
    if smscode != real_sms_code:
        return jsonify(errno=RET.PARAMERR, errmsg="验证码填写错误")

    # 3.3 一致：根据User模型创建用户对象,保存到数据库
    # 创建用户对象 给对象属性赋值
    user = User()
    user.mobile = mobile
    user.nick_name = mobile
    # TODO: 需要将密码加密后赋值给password_hash
    # user.password_hash
    user.password = password
    # 记录用户最后一次登录时间
    user.last_login = datetime.now()

    try:
        db.session.add(user)
        db.session.commit()
    except Exception as error:
        current_app.logger.error(error)
        db.session.rollback()
        return jsonify(errno=RET.DBERR, errmsg="保存用户对象到数据库异常")

    # 3.4 一般：用户注册成功 第一次都会给用户自动登入, 使用session存储用户信息
    session["user_id"] = user.id
    session["mobile"] = user.mobile
    session["nick_name"] = user.nick_name

    # 4.返回值处理
    return jsonify(errno=RET.OK, errmsg="注册成功")


@passport_blu.route('/image_code')
def get_imagecode():
    """获取验证码图片"""
    # 1.获取参数
    # 1.1 获取前段出上来的随机UUID编码 imageCodeID
    imageCodeID = request.args.get("imageCodeId")

    # 2.效验参数
    # 2.2 UUID编码 imageCodeId是否为空
    if not imageCodeID:
        abort(404)

    # 3.逻辑处理
    # 3.1 生成验证码图片对象, 验证码的真实值
    name, text, image = captcha.generate_captcha()

    try:
        # 3.2 将图片验证码真实值使用编码存储到redis
        redis_store.set("imagecode_%s" %imageCodeID, text, ex=constants.IMAGE_CODE_REDIS_EXPIRES)
    except Exception as e:
        abort(500)

    # 4.返回值处理
    # 4.1 将图片对象返回给前段展示
    # 创建响应对象
    response = make_response(image)
    # 设置响应对象的响应头中返回值类型
    response.headers["Content-Type"] = "image/jpeg"
    return response


# POST:127.0.0.1:5000/passport/sms_code
@passport_blu.route('/sms_code', methods=["POST"])
def send_sms():
    """点击发送短信验证码接口"""
    """
    1.获取参数
    1.1  手机号码 用户填写的验证码值 UUID随机值编号
    2.校验参数
    2.1 手机号码 用户填写的验证码值 UUID随机值编号判空校验
    2.2 手机号码格式是否正确
    3.逻辑处理
    3.1 根据image_code_id编号去redis中获取验证码的真实值
    3.1.1 real_image_code 验证码的真实值有值： 删除Redis中的存储的值 防止多次利用real_image_code来验证
    3.1.2 没有值： 代表redis中验证码值过期了 --> 细节： 调用前端的再次生成一张图片

    3.2 比较用户填写的image_code和后端真实的real_image_code进行对比
    成功：发送短信验证码
    失败：验证码填写错误

    3.3.0 根据手机号码去查询手机号码是否注册过

    3.3 调用云通讯的SDK发送短信验证码
    3.3.1 生成一个6位的随机短信内容

    3.4 发送短信验证码成功
    3.4.1 存储短信验证码到redis

    4.返回值处理 告知前端发送短信验证成功请注意查收
    """

    # 1.1 手机号码 用户填写的验证码值 UUID随机值编号
    # request.json 直接将前端上传的json字符串转换成python对象（dict or list）
    param_dict = request.json
    mobile = param_dict.get("mobile")
    image_code = param_dict.get("image_code")
    image_code_id = param_dict.get("image_code_id")

    # 2.1 手机号吗 用户填写的验证码值 UUID随机值编号判空
    if not all([mobile, image_code, image_code_id]):
        # 返回错误的json字符串
        return jsonify(errno=RET.PARAMERR, errmsg="参数不足")
    if not re.match('1[356789][0-9]{9}', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg="手机格式错误")

    try:
        # 3.1 根据image_code_id编号取redis中获取验证码的真实值
        real_image_code = redis_store.get("imagecode_%s" % image_code_id)

        # 3.1.1 real_image_code 验证码的真实值有值： 删除redis中的存储的值 防止多次利用real_image_code
        if real_image_code:
            redis_store.delete(real_image_code)
    except Exception as e:
        return jsonify(errno=RET.DBERR, errmsg="查询验证码的真实值异常")

    # 3.1.2 没有值： 代表redis中验证码过期了 调用前段的再次生产一张图片
    if  not real_image_code:
        return jsonify(errno=RET.NODATA, errmsg="验证码过期")

    # 3.2 比较用户填写的image_code和后端真实的real_image_code进行对比
    if image_code.lower() != real_image_code.lower():
        # 失败： 验证码填写错误
        return jsonify(errno=RET.DATAERR, errmsg="验证码填写错误")

    try:
        # 3.3.0 根据手机号码去查询手机号码是否被注册过
        user = User.query.filter_by(mobile=mobile).first()
        if user:
            # 有值表示注册过了
            return jsonify(errno=RET.DATAEXIST, errmsg="手机号码被注册过")
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="查询用户对象异常")

    # 3.3 调用云通讯的SDK发送短信验证码
    # 3.3.1 生产一个6为的随机短信内容
    sms_code = random.randint(0, 999999)
    # 生成6位耳朵随机值不够补0
    sms_code = "%06d" %sms_code

    result = CCP().send_template_sms(mobile, [sms_code, constants.IMAGE_CODE_REDIS_EXPIRES/60], 1)

    if result != 0:
        # 发送短信验证码失败
        return jsonify(errno=RET.THIRDERR, errmsg="发送短信验证码失败")

    # 3.4 发送短信验证码成功
    # 3.4.1 存储短信验证码到redis
    try:
        redis_store.set("SMS_%s" %mobile, sms_code, ex=constants.SMS_CODE_REDIS_EXPIRES)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg="保存短信验证码到数据库异常")

    # 4 返回值处理 告知前段发送短信验证成功请注意查收
    return jsonify(errno=RET.OK, errmsg="发送短信验证成功")















